package com.proxy.server.frontend.dispatcher;

import com.proxy.common.constant.ErrorCodeType;
import com.proxy.common.constant.PacketType;
import com.proxy.common.constant.SqlType;
import com.proxy.common.model.PacketReader;
import com.proxy.common.packet.BinaryPacket;
import com.proxy.common.packet.ErrorPacket;
import com.proxy.common.utils.CharsetUtil;
import com.proxy.router.SQLRouteEngine;
import com.proxy.router.SQLRouteResult;
import com.proxy.server.frontend.session.ProxySession;
import com.proxy.server.context.AppContext;
import com.proxy.server.frontend.handler.FrontendHandler;
import com.proxy.server.frontend.session.Session;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.nio.charset.Charset;

/**
 * 所有过程都在这里分发
 * Created by liufish on 16/12/19.
 */
public class Dispatcher {


    private static final Logger logger = LogManager.getLogger(Dispatcher.class);

    private static DispatcherDetail detail = new DispatcherDetail();

    private static SQLRouteEngine routeEngine = new SQLRouteEngine();

    /**
     * 构造函数完成基本分发
     * 把最简单的语句分发,复杂的再跳转到本类来写
     */
    public static void dispatch(final BinaryPacket bin, final Session session){

        AppContext.getInstance().getProxyExecutor().execute(new Runnable() {
            @Override
            public void run() {
                switch (bin.body[0]) {
                    case PacketType.COM_SLEEP://内部线程状态●
                        FrontendHandler.comSleep(bin, session);
                        break;
                    case PacketType.COM_QUIT://关闭连接★
                        FrontendHandler.comQuit(bin, session);
                        break;
                    case PacketType.COM_INIT_DB://切换数据库★
                        FrontendHandler.comInitDb(bin, session);
                        break;
                    case PacketType.COM_QUERY://SQL查询请求★★★★★
                        FrontendHandler.comQuery(bin, session);
                        break;
                    case PacketType.COM_FIELD_LIST://获取字段信息●
                        FrontendHandler.comFieldList(bin, session);
                        break;
                    case PacketType.COM_CREATE_DB://创建数据库●
                        FrontendHandler.comCreateDb(bin, session);
                        break;
                    case PacketType.COM_DROP_DB://删除数据库●
                        FrontendHandler.comDropDb(bin, session);
                        break;
                    case PacketType.COM_REFRESH://清除缓存●
                        FrontendHandler.comRefresh(bin, session);
                        break;
                    case PacketType.COM_SHUTDOWN://停止服务器●
                        FrontendHandler.comShutdown(bin, session);
                        break;
                    case PacketType.COM_STATISTICS://获取服务器统计信息●
                        FrontendHandler.comStatistics(bin, session);
                        break;
                    case PacketType.COM_PROCESS_INFO://获取当前连接的列表●
                        FrontendHandler.comProcessInfo(bin, session);
                        break;
                    case PacketType.COM_CONNECT://内部线程状态●
                        FrontendHandler.comConnect(bin, session);
                        break;
                    case PacketType.COM_PROCESS_KILL://中断某个连接★  // TODO: 16/12/19
                        FrontendHandler.comProcessKill(bin, session);
                        break;
                    case PacketType.COM_DEBUG://保存服务器调试信息●
                        FrontendHandler.comDebug(bin, session);
                        break;
                    case PacketType.COM_PING://测试连通性★
                        FrontendHandler.comPing(bin, session);
                        break;
                    case PacketType.COM_TIME://内部线程状态●
                        FrontendHandler.comTime(bin, session);
                        break;
                    case PacketType.COM_DELAYED_INSERT://内部线程状态●
                        FrontendHandler.comDelayedInsert(bin, session);
                        break;
                    case PacketType.COM_CHANGE_USER://重新登陆●
                        FrontendHandler.comChangeUser(bin, session);
                        break;
                    case PacketType.COM_BINLOG_DUMP://获取二进制日志信息●
                        FrontendHandler.comBinlogDump(bin, session);
                        break;
                    case PacketType.COM_TABLE_DUMP://获取数据表结构信息●
                        FrontendHandler.comTableDump(bin, session);
                        break;
                    case PacketType.COM_CONNECT_OUT://内部线程状态●
                        FrontendHandler.comConnectOut(bin, session);
                        break;
                    case PacketType.COM_REGISTER_SLAVE://从服务器向主服务器进行注册●
                        FrontendHandler.comRegisterSlave(bin, session);
                        break;
                    case PacketType.COM_STMT_PREPARE:  //预处理SQL语句 //// TODO: 16/12/19  
                        FrontendHandler.comStmtPrepare(bin, session);
                        break;
                    case PacketType.COM_STMT_EXECUTE: //执行预处理语句//// TODO: 16/12/19  
                        FrontendHandler.comStmtExecute(bin, session);
                        break;
                    case PacketType.COM_STMT_SEND_LONG_DATA://预处理 不支持●
                        FrontendHandler.comStmtSendLongData(bin, session);
                        break;
                    case PacketType.COM_STMT_CLOSE://销毁预处理语句// // TODO: 16/12/19  
                        FrontendHandler.comStmtClose(bin, session);
                        break;
                    case PacketType.COM_STMT_RESET://清除预处理语句参数缓存//// TODO: 16/12/19
                        FrontendHandler.comStmtReset(bin, session);
                        break;
                    case PacketType.COM_SET_OPTION://设置语句选项●
                        FrontendHandler.comSetOption(bin, session);
                        break;
                    case PacketType.COM_STMT_FETCH://获取预处理语句的执行结果//// TODO: 16/12/19  
                        FrontendHandler.comStmtFetch(bin, session);
                        break;
                    default:
                        FrontendHandler.unSupport(bin, session);
                        break;
                }

            }
        });
    }

    /**
     * 非预处理增删查改等核心语句.
     */
    public static void dispatcherQuery(BinaryPacket bin, Session session){

        String sql = null;
        String charset = null;
        try {
            charset = CharsetUtil.getCharset(session.getFrontendConnection().getCharsetIndex());
            sql = new PacketReader(bin.body).readString(charset);

            logger.debug("查询的sql为 : "+ sql);
        }catch (Exception ex){
            //需要重写错误类型
            ErrorPacket error = new ErrorPacket();
            error.packetId = ++ bin.packetId ;
            error.errNo = ErrorCodeType.ER_UNKNOWN_CHARACTER_SET;
            String message = String.format("Unknown charset %s", charset);
            error.message = message.getBytes(Charset.forName(charset));
            session.getFrontendConnection().write(error);
            return;
        }

        if(sql == null || sql.length() == 0){
            ErrorPacket error = new ErrorPacket();
            error.packetId = ++ bin.packetId ;
            error.errNo = ErrorCodeType.ER_UNKNOWN_COM_ERROR;
            error.message = "Query unsupported!".getBytes(Charset.forName(charset));
            session.getFrontendConnection().write(error);
            return;
        }

        SQLRouteResult result = routeEngine.route(sql);

        int group = result.getParsedResult().getSqlType().getGroup();

        switch (group) {
            case SqlType.Group.SET:
                detail.setStatement(bin,session,result);
                break;
            case SqlType.Group.SHOW:
                detail.showStatement(bin,session,result);
                break;
            case SqlType.Group.SELECT:
                detail.selectStatement(bin,session,result);
                break;
            case SqlType.Group.USE:
                detail.userStatement(bin,session,result);
                break;
            default:
                logger.debug("ComQueryHandler == Not supported :" + sql);
                break;
        }
    }

    /**
     * 预处理阶段一.
     */
    public static void dispatcherStmtPrepare(BinaryPacket bin, ProxySession session){

    }

    /**
     * 预处理阶段二,增删查改等核心语句.
     */
    public static void dispatcherStmtExecute(BinaryPacket bin, ProxySession session){

    }

    /**
     * 预处理阶段三.
     */
    public static void dispatcherStmtClose(BinaryPacket bin, ProxySession session){

    }
}
